"""
# Copyright 2021 21CN Corporation Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
# -*- coding: utf-8 -*-
import copy
import logging

from core.tosca.map_action import data_mapping, set_inputs
from core.tosca.mapper import ComputeMapper, VduCpMapper, VnfVirtualLinkMapper, VirtualStorageMapper, \
    SecurityGroupMapper, SecurityGroupRuleMapper

logger = logging.getLogger()


def translate(app_description, sw_image_map):
    """
    翻译tosca为hot
    Args:
        app_description: tosca模板
        sw_image_map: 镜像名称id映射

    Returns: hot

    """
    hot = {
        'heat_template_version': '2016-10-14',
        'description': 'Generated By OsPlugin',
        'parameters': {},
        'resources': {},
        'outputs': {}
    }
    topology_template = app_description['topology_template']

    # 翻译 参数声明
    hot['parameters'] = translate_inputs(topology_template['inputs'])

    # 翻译 节点声明
    node_templates = topology_template['node_templates']
    for name, node_template in node_templates.items():
        resource_translator = NODE_TEMPLATE_MAPPER.get(node_template['type'], translate_unknown)
        resource_translator(node_template,
                            app_d=app_description,
                            hot=hot,
                            node_name=name,
                            sw_image_map=sw_image_map)

    # 翻译 组声明
    groups = topology_template['groups']
    for name, group in groups.items():
        group_translator = GROUP_MAPPER.get(group['type'], translate_unknown)
        group_translator(group,
                         app_d=app_description,
                         hot=hot,
                         node_name=name)

    # 翻译 策略声明
    policies = topology_template['policies']
    for policy_obj in policies:
        name = next(iter(policy_obj.keys()))
        policy = policy_obj[name]
        policy_translator = POLICY_MAPPER.get(policy['type'], translate_unknown)
        policy_translator(policy, app_d=app_description, hot=hot, node_name=name)

    # 翻译 函数
    for resource in hot['resources'].values():
        translate_function(resource['properties'])

    return hot


def translate_inputs(inputs):
    """
    把 inputs 翻译为 parameters
    hot不支持text、password类型参数，翻译为string
    Args:
        inputs: tosca参数模版

    Returns: parameters

    """
    parameters = copy.deepcopy(inputs)
    for key in parameters.keys():
        if parameters[key]['type'] == 'text' or parameters[key]['type'] == 'password':
            parameters[key]['type'] = 'string'
    return parameters


def translate_vdu_compute(node_template, **kwargs):
    resource = {
        'type': 'OS::Nova::Server',
        'properties': {}
    }
    data_mapping(ComputeMapper, node_template, resource['properties'], **kwargs)
    node_templates = kwargs['app_d']['topology_template']['node_templates']

    resource['properties']['networks'] = []
    for node_name, node_template in node_templates.items():
        if node_template['type'] != 'tosca.nodes.nfv.VduCp':
            continue
        if 'requirements' not in node_template:
            continue
        for requirement in node_template['requirements']:
            if 'virtual_binding' not in requirement:
                continue
            if kwargs['node_name'] != requirement['virtual_binding']:
                continue
            resource['properties']['networks'].append({
                'port': {
                    'get_resource': node_name
                }
            })

    node_name = kwargs['node_name']
    kwargs['hot']['outputs'][node_name] = {
        'value': {
            'vmId': {
                'get_resource': node_name,
            },
            'networks': {
                'get_attr': [node_name, 'addresses']
            }
        }
    }
    kwargs['hot']['resources'][kwargs['node_name']] = resource

    return resource


def translate_vdu_cp(node_template, **kwargs):
    resource = {
        'type': 'OS::Neutron::Port',
        'properties': {}
    }
    data_mapping(VduCpMapper, node_template, resource['properties'], **kwargs)

    kwargs['hot']['resources'][kwargs['node_name']] = resource
    return resource


def translate_virtual_link(node_template, **kwargs):
    """
    网络不通过heat创建，翻译为neutron network
    Args:
        node_template: 网络模板
        **kwargs: 其他

    Returns: neutron network

    """
    network = {}
    data_mapping(VnfVirtualLinkMapper, node_template, network, **kwargs)
    subnets = network.pop('subnets', [])
    virtual_link = {'network': network, 'subnets': subnets}

    if 'inputs' in kwargs:
        inputs = kwargs['inputs']
        parameters = kwargs.get('parameters', None)
        set_inputs(virtual_link, inputs, parameters)
    return virtual_link


def translate_virtual_storage(node_template, **kwargs):
    resource = {
        'type': 'OS::Cinder::Volume',
        'properties': {}
    }
    data_mapping(VirtualStorageMapper, node_template, resource['properties'], **kwargs)

    kwargs['hot']['resources'][kwargs['node_name']] = resource
    return resource


def translate_port_security_group(group, **kwargs):
    resources = kwargs['hot']['resources']
    node_name = kwargs['node_name']
    for port in group['members']:
        properties = resources[port]['properties']
        if 'security_groups' not in properties:
            properties['security_groups'] = []
        properties['security_groups'].append({
            'get_resource': node_name
        })

    resource = {
        'type': 'OS::Neutron::SecurityGroup',
        'properties': {
            'rules': [{
                'remote_mode': 'remote_group_id'
            }]
        }
    }
    data_mapping(SecurityGroupMapper, group, resource['properties'], **kwargs)
    resources[node_name] = resource
    return resource


def translate_security_group_rule(policy, **kwargs):
    resource = {
        'type': 'OS::Neutron::SecurityGroupRule',
        'properties': {}
    }
    data_mapping(SecurityGroupRuleMapper, policy, resource['properties'], **kwargs)
    rule_name = kwargs['node_name']
    resources = kwargs['hot']['resources']
    for group_name in policy['targets']:
        resources[group_name + '_rule_' + rule_name] = copy.deepcopy(resource)
        resources[group_name + '_rule_' + rule_name]['properties']['security_group'] = {
            'get_resource': group_name
        }
    return resource


def translate_placement_group(placement_group, **kwargs):
    """
    翻译 placement group
    Args:
        placement_group:
        **kwargs:

    Returns:

    """
    resources = kwargs['hot']['resources']
    node_name = kwargs['node_name']
    for server in placement_group['members']:
        properties = resources[server]['properties']
        if 'scheduler_hints' not in properties:
            properties['scheduler_hints'] = {}
        properties['scheduler_hints']['group'] = {
            'get_resource': node_name
        }

    resource = {
        'type': 'OS::Nova::ServerGroup',
        'properties': {
            'policies': []
        }
    }
    resources[node_name] = resource
    return resource


def translate_schedule_rule(policy, **kwargs):
    """

    Args:
        policy:
        **kwargs:

    Returns:

    """
    if policy['type'] == 'tosca.policies.nfv.AffinityRule':
        key = 'affinity'
    elif policy['type'] == 'tosca.policies.nfv.AntiAffinityRule':
        key = 'anti-affinity'
    else:
        translate_unknown(policy, **kwargs)
        return
    if not policy['properties']['enforced']:
        key = 'soft-' + key
    resources = kwargs['hot']['resources']
    for group_name in policy['targets']:
        properties = resources[group_name]['properties']
        if key not in set(properties['policies']):
            properties['policies'].append(key)


def translate_function(properties):
    """
    翻译函数名称和实现
    目前支持的函数
    get_input: 翻译为 get_param ，参数列表不变
    concat: 翻译为 list_join ，参数列表变更为 ['', 原参数]
    其他函数有需求可实现
    Args:
        properties: 可能包含函数的obj对象

    Returns:

    """
    if isinstance(properties, dict):
        if 'get_input' in properties:
            properties['get_param'] = properties.pop('get_input')
        if 'concat' in properties:
            properties['list_join'] = ['', properties.pop('concat')]
        for value in properties.values():
            translate_function(value)

    elif isinstance(properties, list):
        for item in properties:
            translate_function(item)


def translate_unknown(unknown, **kwargs):
    """
    跳过翻译不支持的类型
    Args:
        unknown: 类型数据
        **kwargs: 其他

    Returns:

    """
    logger.info('skip translate unknown type %s', unknown['type'])


NODE_TEMPLATE_MAPPER = {
    'tosca.nodes.nfv.Vdu.Compute': translate_vdu_compute,
    'tosca.nodes.nfv.Vdu.VirtualStorage': translate_virtual_storage,
    'tosca.nodes.nfv.VduCp': translate_vdu_cp,
}

GROUP_MAPPER = {
    'tosca.groups.nfv.PortSecurityGroup': translate_port_security_group,
    # todo 未测试的亲和性调度
    # 'tosca.groups.nfv.PlacementGroup': translate_placement_group
}

POLICY_MAPPER = {
    'tosca.policies.nfv.SecurityGroupRule': translate_security_group_rule,
    # todo 未测试的亲和性调度
    # 'tosca.policies.nfv.AffinityRule': translate_schedule_rule,
    # 'tosca.policies.nfv.AntiAffinityRule': translate_schedule_rule
}
